package com.example.exampleOS.slice;

import com.example.exampleOS.MainAbility;
import com.example.exampleOS.ResourceTable;
import com.example.exampleOS.locator.LocationBean;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.Text;
import ohos.agp.window.dialog.ToastDialog;
import ohos.app.Context;
import ohos.bundle.IBundleManager;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.location.*;
import ohos.sensor.agent.CategoryOrientationAgent;
import ohos.sensor.bean.CategoryOrientation;
import ohos.sensor.data.CategoryOrientationData;
import ohos.sensor.listener.ICategoryOrientationDataCallback;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class LocationSlice extends AbilitySlice {
    //    定位start
    private static final String TAG = MainAbility.class.getSimpleName();
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD000F00, TAG);
    private static final int EVENT_ID = 0x12;
    private static final String PERM_LOCATION = "ohos.permission.LOCATION";
    private static final long SAMPLING_INTERVAL_NANOSECONDS = 500000000;
    private static String FORMAT_DEGREE = "%.2f";
    private LocatorResult locatorResult = new LocatorResult();
    private Context context;
    private Locator locator;
    private RequestParam requestParam;
    private GeoConvert geoConvert;
    private List<GeoAddress> gaList;
    private LocationBean locationDetails;
    private Text geoAddressInfoText;
    private Text locationInfoText;
    private CategoryOrientationAgent categoryOrientationAgent;
    private Text compassAngleText;
    private float degree;
    private ICategoryOrientationDataCallback categoryOrientationDataCallback;
    //    定位 end
    @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_location);
;
//        定位 start
        initComponents();
        register(this);
        initCompass();
        //定位 end
    }
    private String getRotation(float degree) {
        if (degree >= 0 && degree <= 22.5) {
            return String.format(Locale.ENGLISH, FORMAT_DEGREE, degree) + " N";
        } else if (degree > 22.5 && degree <= 67.5) {
            return String.format(Locale.ENGLISH, FORMAT_DEGREE, degree) + " NE";
        } else if (degree > 67.5 && degree <= 112.5) {
            return String.format(Locale.ENGLISH, FORMAT_DEGREE, degree) + " E";
        } else if (degree > 112.5 && degree <= 157.5) {
            return String.format(Locale.ENGLISH, FORMAT_DEGREE, degree) + " SE";
        } else if (degree > 157.5 && degree <= 202.5) {
            return String.format(Locale.ENGLISH, FORMAT_DEGREE, degree) + " S";
        } else if (degree > 202.5 && degree <= 247.5) {
            return String.format(Locale.ENGLISH, FORMAT_DEGREE, degree) + " SW";
        } else if (degree > 247.5 && degree <= 282.5) {
            return String.format(Locale.ENGLISH, FORMAT_DEGREE, degree) + " W";
        } else if (degree > 282.5 && degree <= 337.5) {
            return String.format(Locale.ENGLISH, FORMAT_DEGREE, degree) + " NW";
        } else if (degree > 337.5 && degree <= 360.0) {
            return String.format(Locale.ENGLISH, FORMAT_DEGREE, degree) + " N";
        } else {
            return "/";
        }
    }

    private void initCompass() {

        if (findComponentById(ResourceTable.Id_compass_angle_text) instanceof Text) {
            compassAngleText = (Text) findComponentById(ResourceTable.Id_compass_angle_text);
        }

        categoryOrientationAgent = new CategoryOrientationAgent();
        CategoryOrientation categoryOrientation =
                categoryOrientationAgent.getSingleSensor(CategoryOrientation.SENSOR_TYPE_ORIENTATION);
        categoryOrientationDataCallback =
                new ICategoryOrientationDataCallback() {
                    @Override
                    public void onSensorDataModified(CategoryOrientationData categoryOrientationData) {
                        degree = categoryOrientationData.getValues()[0];
                        handler.sendEvent(0);
                    }

                    @Override
                    public void onAccuracyDataModified(CategoryOrientation categoryOrientation, int var1) {}

                    @Override
                    public void onCommandCompleted(CategoryOrientation categoryOrientation) {}
                };
        categoryOrientationAgent.setSensorDataCallback(
                categoryOrientationDataCallback, categoryOrientation, SAMPLING_INTERVAL_NANOSECONDS);
    }

    private void initComponents() {
        if (findComponentById(ResourceTable.Id_start_locating) instanceof Button) {
            Button startLocatingButton = (Button) findComponentById(ResourceTable.Id_start_locating);
            startLocatingButton.setClickedListener(component -> registerLocationEvent());
        }
        if (findComponentById(ResourceTable.Id_stop_locating) instanceof Button) {
            Button stopLocatingButton = (Button) findComponentById(ResourceTable.Id_stop_locating);
            stopLocatingButton.setClickedListener(component -> unregisterLocationEvent());
        }
        if (findComponentById(ResourceTable.Id_location_info) instanceof Text) {
            locationInfoText = (Text) findComponentById(ResourceTable.Id_location_info);
        }
        if (findComponentById(ResourceTable.Id_geo_address_info) instanceof Text) {
            geoAddressInfoText = (Text) findComponentById(ResourceTable.Id_geo_address_info);
        }
    }

    private void notifyLocationChange(LocationBean locationDetails) {
        update(locationDetails);
    }

    private void update(LocationBean locationDetails) {
        locationInfoText.setText("");
        locationInfoText.append("纬度 : " + locationDetails.getLatitude() + System.lineSeparator());
        locationInfoText.append("经度 : " + locationDetails.getLongitude() + System.lineSeparator());
        locationInfoText.append("速度 : " + locationDetails.getSpeed() + " m/s" + System.lineSeparator());
        locationInfoText.append("方向 : " + locationDetails.getDirection() + System.lineSeparator());
        locationInfoText.append("时间 : " + locationDetails.getTime());

        geoAddressInfoText.setText("");
        geoAddressInfoText.append("子行政区域 : " + locationDetails.getSubAdministrative() + System.lineSeparator());
        geoAddressInfoText.append("道路名 : " + locationDetails.getRoadName() + System.lineSeparator());
        geoAddressInfoText.append("城市 : " + locationDetails.getLocality() + System.lineSeparator());
        geoAddressInfoText.append("行政区域 : " + locationDetails.getAdministrative() + System.lineSeparator());
        geoAddressInfoText.append("国家 : " + locationDetails.getCountryName());
    }

    private EventHandler handler = new EventHandler(EventRunner.current()) {
        @Override
        protected void processEvent(InnerEvent event) {
            if (event.eventId == EVENT_ID) {
                notifyLocationChange(locationDetails);
            }
            compassAngleText.setText(getRotation(degree));
        }
    };

    private void register(Context ability) {
        context = ability;
        requestPermission(PERM_LOCATION);
    }

    private void registerLocationEvent() {
        if (hasPermissionGranted(PERM_LOCATION)) {
            int timeInterval = 0;
            int distanceInterval = 0;
            locator = new Locator(context);
            requestParam = new RequestParam(RequestParam.PRIORITY_LOW_POWER, timeInterval, distanceInterval);
            locator.startLocating(requestParam, locatorResult);
        }
    }

    private void unregisterLocationEvent() {
        if (locator != null) {
            locator.stopLocating(locatorResult);
        }
    }

    private boolean hasPermissionGranted(String permission) {
        return context.verifySelfPermission(permission) == IBundleManager.PERMISSION_GRANTED;
    }

    private void requestPermission(String permission) {
        if (context.verifySelfPermission(permission) != IBundleManager.PERMISSION_GRANTED) {
            context.requestPermissionsFromUser(new String[] {permission}, 0);
        }
    }

    private class LocatorResult implements LocatorCallback {
        @Override
        public void onLocationReport(Location location) {
            HiLog.info(
                    LABEL_LOG,
                    "%{public}s",
                    "onLocationReport : " + location.getLatitude() + "-" + location.getAltitude());
            setLocation(location);
        }

        @Override
        public void onStatusChanged(int statusCode) {
            HiLog.info(LABEL_LOG, "%{public}s", "MyLocatorCallback onStatusChanged : " + statusCode);
        }

        @Override
        public void onErrorReport(int errorCode) {
            HiLog.info(LABEL_LOG, "%{public}s", "MyLocatorCallback onErrorReport : " + errorCode);
        }
    }

    private void setLocation(Location location) {
        if (location != null) {
            Date date = new Date(location.getTimeStamp());
            locationDetails = new LocationBean();
            locationDetails.setTime(date.toString());
            locationDetails.setLatitude(location.getLatitude());
            locationDetails.setLongitude(location.getLongitude());
            locationDetails.setPrecision(location.getAccuracy());
            locationDetails.setSpeed(location.getSpeed());
            locationDetails.setDirection(location.getDirection());
            fillGeoInfo(locationDetails, location.getLatitude(), location.getLongitude());
            handler.sendEvent(EVENT_ID);
            gaList.clear();
        } else {
            HiLog.info(LABEL_LOG, "%{public}s", "EventNotifier or Location response is null");
            new ToastDialog(context).setTitleText("EventNotifier or ocation response is null").show();
        }
    }

    private boolean fillGeoInfo(LocationBean locationDetails, double geoLatitude, double geoLongitude) {
        if (geoConvert == null) {
            geoConvert = new GeoConvert();
        }
        if (geoConvert.isGeoAvailable()) {
            try {
                gaList = geoConvert.getAddressFromLocation(geoLatitude, geoLongitude, 1);
                if (!gaList.isEmpty()) {
                    GeoAddress geoAddress = gaList.get(0);
                    setGeo(locationDetails, geoAddress);
                    return true;
                }
            } catch (IllegalArgumentException | IOException e) {
                HiLog.error(LABEL_LOG, "%{public}s", "fillGeoInfo exception");
            }
        }
        return false;
    }

    private void setGeo(LocationBean locationDetails, GeoAddress geoAddress) {
        locationDetails.setRoadName(checkIfNullOrEmpty(geoAddress.getRoadName()));
        locationDetails.setLocality(checkIfNullOrEmpty(geoAddress.getLocality()));
        locationDetails.setSubAdministrative(checkIfNullOrEmpty(geoAddress.getSubAdministrativeArea()));
        locationDetails.setAdministrative(checkIfNullOrEmpty(geoAddress.getAdministrativeArea()));
        locationDetails.setCountryName(checkIfNullOrEmpty(geoAddress.getCountryName()));
    }

    private String checkIfNullOrEmpty(String value) {
        if (value == null || value.isEmpty()) {
            return "NA";
        }
        return value;
    }

    @Override
    protected void onStop() {
        super.onStop();
        categoryOrientationAgent.releaseSensorDataCallback(categoryOrientationDataCallback);
        handler.removeAllEvent();
    }
}
